package com.sos.logging
{
	import com.sos.DebugOptions;
	import com.sos.utils.StringUtils;
	import com.sos.utils.Utils;
	
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;

	public class Log
	{
		
		// -------------------------------------------------------------------------------
		// CONSTANTS
		// -------------------------------------------------------------------------------
		
		// Multiline separator
		public static const		MULTILINE_SEPARATOR:String		= "§§§";
		
		// Format text constants 
		public static const		FORMAT_PAD_CHAR:String			= ":";
		public static const		FORMAT_PAD_HEADER:String		= "::: ";
		public static const		FORMAT_PAD_LENGTH:int			= 85;
		
		// -------------------------------------------------------------------------------
		// FIELDS
		// -------------------------------------------------------------------------------
		
		private static var _initialized:Boolean = false;
		private static var _options:LogOptions;
		private static var _targets:Dictionary;
		private static var _dispatcher:EventDispatcher;

		// -------------------------------------------------------------------------------
		// CONSTRUCTORS
		// -------------------------------------------------------------------------------
		
		// Fake static constructor
		{
			// Targets list
			_targets = new Dictionary();
			// Dispatcher
			_dispatcher = new EventDispatcher();
			// Default options
			_options = null;
			// Init!
			_initialized = true;
		}
		
		// -------------------------------------------------------------------------------
		// CONSTRUCTORS
		// -------------------------------------------------------------------------------
		
		public function Log()
		{
			// Safe unique instance lock
			if (_initialized) throw new Error("Log singleton already created!");
		}
		
		// -------------------------------------------------------------------------------
		// PROPERTIES
		// -------------------------------------------------------------------------------
		
		public static function get isInit():Boolean
		{
			return _options != null;
		}
		
		// -------------------------------------------------------------------------------
		// PPRIVATE METHODS
		// -------------------------------------------------------------------------------
		
		private static function log(level:LogLevel, msg:String, ... rest):void
		{
			// we don't want to allow people to log messages at the 
			// Log.Level.ALL level, so throw a RTE if they do
			if (!level.valid(LogLevel.DEBUG))
			{
				throw new ArgumentError("LogLevel.DEBUG is the minimum usable LogLevel");
			}
			// Check for registered listeneres
			if (_dispatcher.hasEventListener(LogEvent.LOG))
			{
				/*
				// replace all of the parameters in the msg string
				for (var i:int = 0; i < rest.length; i++)
				{
					msg = msg.replace(new RegExp("\\{"+i+"\\}", "g"), rest[i]);
				}
				*/
				// Replace all of the parameters in the msg string
				msg = StringUtils.substitute(msg, Utils.removeRestNesting(rest));
				// Dispatch 
				_dispatcher.dispatchEvent(new LogEvent(level, msg));
			}
		}
		
		// -------------------------------------------------------------------------------
		// PUBLIC METHODS
		// -------------------------------------------------------------------------------
		
		public static function init(options:LogOptions):void
		{
			// Target initialization
			for (var i:int = 0; i < options.targets.length; i++)
			{
				// Get the options
				var params:LogTargetParams = options.targets[i];
				// Creates the LogTarget
				var target:LogTarget = LogTargetFactory.getInstance(params.type, params);
				// Register a the target
				register(target);
			}
			// Saves the options
			_options = options;
		}
		
		public static function getTarget(type:LogTargetType):LogTarget
		{
			return _targets[type] as LogTarget;
		}
		
		public static function registered(type:LogTargetType):Boolean
		{
			return _targets[type] != null;
		}
		
		public static function register(target:LogTarget):void
		{
			// Check if LogTarget is already registered
			if (!registered(target.type))
			{
				// Register the target and adds the listener
				_targets[target.type]= target;
				_dispatcher.addEventListener(LogEvent.LOG, target.logEvent);
			}
			else throw new ArgumentError("LogTarget " + target.type.name + "is already registered");
		}
		
		public static function unregister(type:LogTargetType):void
		{
			// Check if LogTarget is already registered
			if (registered(type))
			{
				// Removes the listener 
				_dispatcher.removeEventListener(LogEvent.LOG, _targets[type].logEvent);
				// Delete the reference from targets list
				delete _targets[type];
			}
			else throw new ArgumentError("LogTarget " + type.name + "is not registered");
		}
		
		public static function enable(type:LogTargetType):void
		{
			// Check if LogTarget is already registered
			if (registered(type))
			{
				(_targets[type] as LogTarget).enabled = true;
			}
			else throw new ArgumentError("LogTarget " + type.name + "is not registered");
		}
		
		public static function disable(type:LogTargetType):void
		{
			// Check if LogTarget is already registered
			if (registered(type))
			{
				(_targets[type] as LogTarget).enabled = false;
			}
			else throw new ArgumentError("LogTarget " + type.name + "is not registered");
		}
		
		public static function info(message:String, ... args):void
		{
			log(LogLevel.INFO, message, Utils.removeRestNesting(args));
		}
		
		public static function debug(message:String, ... args):void
		{
			log(LogLevel.DEBUG, message, Utils.removeRestNesting(args));
		}
		
		public static function warn(message:String, ... args):void
		{
			log(LogLevel.WARN, message, Utils.removeRestNesting(args));
		}
		
		public static function error(message:String, ... args):void
		{
			log(LogLevel.ERROR, message, Utils.removeRestNesting(args));
		}
		
		public static function fatal(message:String, ... args):void
		{
			log(LogLevel.FATAL, message, Utils.removeRestNesting(args));
		}
		
		/**
		 * Dumps the given IDumpable object into all registered with LogLevel.DEBUG  
		 * @param obj Object to dump
		 */		
		public static function dump(obj:IDumpable):void
		{
			// Create dump container array
			var rows:Array = new Array();
			// Retrieve the object dump
			rows = obj.dump(rows);
			// Dump all rows, logging as 
			for (var i:int = 0; i < rows.length; i++)
			{
				debug(rows[i].toString());
			}
		}
		
	}
}